﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System.Text;
using System.Globalization;
using System.Data;
using RazorEngine;
using RazorEngine.Templating;
using RazorEngine.Configuration;

namespace DbCodeGenerator
{
    class DbCodeUtil
    {
        private dcConfigs dcCfgs;
        private RunSettings runSet;
        public Dictionary<string, List<columnInfo>> tables;
        private Dictionary<string, string> priKeys;
        public CodeTools codeTools;
        public DbCodeUtil(dcConfigs dccfg, RunSettings sets)
        {
            dcCfgs = dccfg;
            runSet = sets;
            getTablesInfo();
            codeTools = new CodeTools();
        }
        public void StartGenerate()
        {
            var config = new TemplateServiceConfiguration();

            var service = RazorEngineService.Create(config);
            Engine.Razor = service;
            //var ones = tmplCfg.Where(item => item.runOne);
            var ones = from TmplInfo info in dcCfgs.tmplList where info.runOne select info;
            foreach (TmplInfo info in ones)
            {
                string tfile = Path.GetFullPath(runSet.tmplDir + "/" + info.tmplFile);
                if (!File.Exists(tfile))
                {
                    continue;
                }
                string ofile = Path.GetFullPath(runSet.tmplDir + "/" + info.outPath + "/" + info.outlFile);
                if (info.onlyCopy)
                {
                    string dir = Path.GetDirectoryName(ofile);
                    if (!Directory.Exists(dir))
                    {
                        Directory.CreateDirectory(dir);
                    }
                    File.Copy(tfile, ofile, true);
                }
                else {
                    getCode(tfile, ofile);
                }
            }
            var tmpls = from TmplInfo info in dcCfgs.tmplList where !info.runOne select info;
            foreach (string tn in runSet.tabs)
            {
                foreach (TmplInfo info in tmpls)
                {
                    string tfile = Path.GetFullPath(runSet.tmplDir + "/" + info.tmplFile);
                    if (!File.Exists(tfile))
                    {
                        continue;
                    }
                    string name = codeTools.humpNaming(runSet.tableNameStyle, tn);
                    name = info.outlFile.Replace("[tabname]", name);
                    string ofile = Path.GetFullPath(runSet.tmplDir + "/" + info.outPath + "/" + name);
                    getCode(tfile, ofile, tn);
                }
            }
        }
        //初始化表信息，主键信息
        private void getTablesInfo()
        {
            tables = new Dictionary<string, List<columnInfo>>();
            priKeys = new Dictionary<string, string>();
            foreach (string tn in runSet.tabs)
            {
                List<columnInfo> cols = new List<columnInfo>();
                DataTable dt = runSet.dbHandle.getColumns(runSet.dbName, tn);
                foreach (DataRow row in dt.Rows)
                {
                    string prikey = row["COLUMN_KEY"] == DBNull.Value ? "" : row["COLUMN_KEY"].ToString();
                    string autoinc = row["EXTRA"] == DBNull.Value ? "" : row["EXTRA"].ToString();
                    ulong size = 0;
                    var len = row["CHARACTER_MAXIMUM_LENGTH"];
                    if (len != DBNull.Value)
                    {
                        Type t = len.GetType();
                        if (t == typeof(Int32))
                        {
                            int tmp = (int)len;
                            if (tmp <= 0)
                            {
                                tmp = 0;
                            }
                            size = ulong.Parse(tmp.ToString());
                        }
                        else
                        {
                            size = (ulong)len;
                        }
                    }
                    columnInfo cinfo = new columnInfo
                    {
                        columnName = row["COLUMN_NAME"].ToString(),
                        columnType = row["DATA_TYPE"].ToString(),
                        PrimaryKey = (prikey.ToUpper() == "PRI"),
                        size = size,
                        autoIncrement = (autoinc == "auto_increment"),
                        defaultValue = "\"\""
                    };
                    if (row["COLUMN_TYPE"].ToString().Contains("unsigned"))
                    {
                        cinfo.columnType = "u" + cinfo.columnType;
                    }
                    var cfg = dcCfgs.typeMapped.Where(x => x.dbtype == cinfo.columnType).FirstOrDefault();
                    if (cfg != null)
                    {
                        cinfo.columnType = cfg.totype;
                    }
                    string dval = row["COLUMN_DEFAULT"].ToString();
                    if (!string.IsNullOrEmpty(dval))
                    {
                        cinfo.defaultValue = dval;
                    }
                    else if (dcCfgs.typeMapped.Where(x => x.dbtype == cinfo.columnType).Count() > 0) {

                        if (!string.IsNullOrEmpty(cfg.defaultValue))
                        {
                            cinfo.defaultValue = cfg.defaultValue;
                        }
                    }

                    cols.Add(cinfo);
                }

                var pricol = cols.Find(col => col.PrimaryKey);
                if (pricol != null)
                {
                    priKeys.Add(tn, pricol.columnName);
                }
                tables.Add(tn, cols);
            }
        }

        /// <summary>
        /// 渲染模板文件
        /// </summary>
        /// <param name="tfile">模板文件</param>
        /// <param name="ofile">输出文件</param>
        /// <param name="tabname"></param>
        /// <returns></returns>
        private bool getCode(string tfile, string ofile, string tabname = "")
        {
            string dir = Path.GetDirectoryName(ofile);
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            string template = File.ReadAllText(tfile, System.Text.Encoding.UTF8);
            var result = "";
            List<columnInfo> tabinfo = new List<columnInfo>();
            string prikey = "";
            string filename = "";
            if (!string.IsNullOrEmpty(tabname))
            {
                filename = codeTools.humpNaming(runSet.tableNameStyle, tabname);
                tabinfo = tables[tabname];
                var priInfo = tabinfo.Find(x => x.PrimaryKey);
                if (priInfo != null)
                {
                    prikey = priInfo.columnName;
                }
            }

            if (!Engine.Razor.IsTemplateCached(tfile, null))
            {
                result = Engine.Razor.RunCompile(template, tfile, null, new { tables = tables, codeTools = codeTools, tabname = tabname, filename = filename, prikey = prikey, tabinfo = tabinfo, runSet= runSet , dcCfgs = dcCfgs });
            }
            else
            {
                result = Engine.Razor.Run(tfile, null, new { tables = tables, codeTools = codeTools, tabname = tabname, filename = filename, prikey = prikey, tabinfo = tabinfo, runSet = runSet, dcCfgs = dcCfgs });
            }
            File.WriteAllText(ofile, result.Replace("<replace>","").Replace("</replace>", ""), System.Text.Encoding.UTF8);
            return true;
        }

    }
}
